struct TimeSpan <: AbstractTimeSpan
    nanoseconds::Int4Span
    months::Int4Span
end

@inline value(x::TimeSpan) = (x.nanoseconds, x.months)
@inline nanoseconds(x::TimeSpan) = x.nanoseconds
@inline months(x::TimeSpan) = x.months

@inline Base.eltype(::Type{TimeSpan}) = Int4Time
TimeSpan(x::TimeSpan) = x

TimeSpan(x::T1, y::T2) where T1<:NanosecondBasedGrain where T2<:MonthBasedGrain = TimeSpan(nanoseconds(x), months(y))
TimeSpan(x::T2, y::T1) where T1<:NanosecondBasedGrain where T2<:MonthBasedGrain = TimeSpan(nanoseconds(y), months(x))
TimeSpan(x::NanosecondBasedGrain) = TimeSpan(nanoseconds(x), zero(Int4Span))
TimeSpan(x::MonthBasedGrain) = TimeSpan(zero(Int4Span), months(x))

Base.zero(::Type{TimeSpan}) = TimeSpan(zero(Int4Span), zero(Int4Span))

TimeSpan(x::Duration) = TimeSpan(value(x), zero(Int4Span))

function TimeSpan(x::Base.Dates.CompoundPeriod)
    result = zero(TimeSpan)
    for p in x.periods
       result = result + p
    end
    return result
end

Base.:(-)(x::TimeSpan) =
    TimeSpan(-nanoseconds(x), -months(x))

Base.signbit(x::TimeSpan) =
    signbit(nanoseconds(x)), signbit(months(x))

Base.sign(x::TimeSpan) =
    sign(nanoseconds(x)), sign(months(x))

Base.copysign(x::TimeSpan, y::TimeSpan) =
    TimeSpan(copysign(nanoseconds(x), nanoseconds(y)), copysign(months(x), months(y)))

Base.flipsign(x::TimeSpan, y::TimeSpan) =
    TimeSpan(flipsign(nanoseconds(x), nanoseconds(y)), flipsign(months(x), months(y)))

Base.abs(x::TimeSpan) =
    if signbit(months(x)) == signbit(nanoseconds(x))
        TimeSpan(abs(nanoseconds(x)), abs(months(x)))
    elseif signbit(months(x))
        TimeSpan(-nanoseconds(x), abs(months(x)))
    else
        x
    end
#=
Base.:(+)(x::TimeSpan, y::TimeSpan) =
    TimeSpan(nanoseconds(x)+nanoseconds(y), months(x)+months(y))
Base.:(-)(x::TimeSpan, y::TimeSpan) =
    TimeSpan(nanoseconds(x)-nanoseconds(y), months(x)-months(y))

Base.:(+)(x::T1, y::T2) where T1<:NanosecondBasedGrain where T2<:MonthBasedGrain =
    TimeSpan(nanoseconds(x), months(y))
Base.:(+)(x::T2, y::T1) where T1<:NanosecondBasedGrain where T2<:MonthBasedGrain =
    TimeSpan(nanoseconds(y), months(x))
Base.:(-)(x::T1, y::T2) where T1<:NanosecondBasedGrain where T2<:MonthBasedGrain =
    TimeSpan(nanoseconds(x), -months(y))
Base.:(-)(x::T2, y::T1) where T1<:NanosecondBasedGrain where T2<:MonthBasedGrain =
    TimeSpan(-nanoseconds(y), months(x))

Base.:(+)(x::Duration, y::T) where T<:NanosecondBasedGrain =
    Duration(nanoseconds(x) + nanoseconds(y))
Base.:(-)(x::Duration, y::T) where T<:NanosecondBasedGrain =
    Duration(nanoseconds(x) - nanoseconds(y))
Base.:(+)(x::Duration, y::T) where T<:MonthBasedGrain =
    TimeSpan(nanoseconds(x), months(y))
Base.:(-)(x::Duration, y::T) where T<:MonthBasedGrain =
    TimeSpan(nanoseconds(x), -months(y))

Base.:(+)(x::TimeSpan, y::T) where T<:NanosecondBasedGrain =
    TimeSpan(nanoseconds(x)+nanoseconds(y), months(x))
Base.:(-)(x::TimeSpan, y::T) where T<:NanosecondBasedGrain =
    TimeSpan(nanoseconds(x)-nanoseconds(y), months(x))
Base.:(+)(x::TimeSpan, y::T) where T<:MonthBasedGrain =
    TimeSpan(nanoseconds(x), months(x)+months(y))
Base.:(-)(x::TimeSpan, y::T) where T<:MonthBasedGrain =
    TimeSpan(nanoseconds(x), months(x)-months(y))
Base.:(+)(x::T, y::TimeSpan) where T<:NanosecondBasedGrain =
    TimeSpan(nanoseconds(x)+nanoseconds(y), months(y))
Base.:(-)(x::T, y::TimeSpan) where T<:NanosecondBasedGrain =
    TimeSpan(nanoseconds(x)-nanoseconds(y), months(y))
Base.:(+)(x::T, y::TimeSpan) where T<:MonthBasedGrain =
    TimeSpan(nanoseconds(y), months(x)+months(y))
Base.:(-)(x::T, y::TimeSpan) where T<:MonthBasedGrain =
    TimeSpan(nanoseconds(y), months(x)-months(y))

Base.:(*)(x::TimeSpan, y::I) where I<:Union{Int64, Int32} =
    TimeSpan(nanoseconds(x) * y, months(x) * y)
Base.:(*)(x::I, y::TimeSpan) where I<:Union{Int64, Int32} =
    TimeSpan(x * nanoseconds(y), x * months(y))


Base.:(+)(x::Duration, y::TimeSpan) = TimeSpan(x) + y
Base.:(+)(x::TimeSpan, y::Duration) = x + TimeSpan(y)
Base.:(-)(x::Duration, y::TimeSpan) = TimeSpan(x) - y
Base.:(-)(x::TimeSpan, y::Duration) = x - TimeSpan(y)

Base.:(+)(x::T, y::Duration) where T<:SimpleClock = (T)(nanoseconds(x) + nanoseconds(y))
Base.:(+)(x::Duration, y::T) where T<:SimpleClock = (T)(nanoseconds(x) + nanoseconds(y))
Base.:(-)(x::T, y::Duration) where T<:SimpleClock = (T)(nanoseconds(x) - nanoseconds(y))
Base.:(-)(x::Duration, y::T) where T<:SimpleClock =
    throw(ErrorException("`Span - Clock` is invalid -- use `Clock - Span`."))
=#
#=
function Base.:(+)(x::T, y::TimeSpan) where T<:SimpleClock
    date = Base.Dates.Date(x)
    year = Base.Dates.year(date)
    nanosecs = (T)(nanoseconds(x) + nanoseconds(y))
    manymonths = Base.Dates.month(date) - 1 + months(y)
    yr, mn = fldmod(manymonths, MONTHS_PER_YEAR)
    mn += copysign(1,mn)
    year += yr
    date = Base.Dates.Date(year, mn)
    return (T)(date) + nanosecs
end
=#
#=
function Base.:(-)(x::T, y::TimeSpan) where T<:SimpleClock
    date = Base.Dates.Date(x)
    year = Base.Dates.year(date)
    nanosecs = (T)(nanoseconds(x) - nanoseconds(y))
    manymonths = Base.Dates.month(date) - 1 - months(y)
    yr, mn = fldmod(manymonths, MONTHS_PER_YEAR)
    mn += copysign(1,mn)
    year += yr
    date = Base.Dates.Date(year, mn)
    return (T)(date) + nanosecs
end
=#
Base.:(-)(x::TimeSpan, y::T) where T<:SimpleClock =
    throw(ErrorException("`TimeSpan - Clock` is invalid -- use `Clock - TimeSpan`."))

function Base.:(+)(x::T, y::TimeSpan) where T<:SimpleClock
    x = x + Months(y)
    x = x + Nanoseconds(y)
    return x
end

Base.:(+)(x::TimeSpan, y::T) where T<:SimpleClock = y + x

function Base.:(-)(x::T, y::TimeSpan) where T<:SimpleClock
    x = x - Months(y)
    x = x - Nanoseconds(y)
    return x
end

function TimeSpan(year::I, month::I=one(I), day::I=zero(I),
                  hour::I=zero(I), minute::I=zero(I), second::I=zero(I),
                  millisecond::I=zero(I), microsecond::I=zero(I),
                  nanosecond::I=zero(I)) where I<:Integer
    dmonth = month
    dmonth += year * MONTHS_PER_YEAR
    dnanosec = nanosecond
    dnanosec += microsecond * NANOSECONDS_PER_MICROSECOND
    dnanosec += millisecond * NANOSECONDS_PER_MILLISECOND
    dnanosec += second * NANOSECONDS_PER_SECOND
    dnanosec += minute * NANOSECONDS_PER_MINUTE
    dnanosec += hour * NANOSECONDS_PER_HOUR
    dnanosec += day * NANOSECONDS_PER_DAY
    return TimeSpan(dnanosec, dmonth)
end
